﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;

namespace Utility3D
{
    public class Viewport
    {
        public Viewport(WorldModel worldModel, Camera camera, double height, double width)
        {
            WorldModel = worldModel;
            Camera = camera;
            Height = height;
            Width = width;
        }

        private Camera Camera { get; set; }
        private WorldModel WorldModel { get; set; }
        private double Height { get; set; }
        private double Width { get; set; }

        public IEnumerable<Action<IDrawing>> Render()
        {
            return WorldModel.DrawingSurfaceChains.SelectMany(Render).ToList();
        }

        private IEnumerable<Action<IDrawing>> Render(DrawingSurface drawingSurface)
        {
            return drawingSurface.WorldModel.Strokes.Select(stroke => Render(stroke, drawingSurface)).Where(action => action != null);
        }

        private Action<IDrawing> Render(Stroke stroke, DrawingSurface drawingSurface)
        {
            var lineStroke = stroke as LineStroke;
            if (lineStroke == null) throw new NotImplementedException("Only supporting line strokes");

            var startPoint3D = lineStroke.Start.GetPoint3DOnSurface(drawingSurface);
            var endPoint3D = lineStroke.End.GetPoint3DOnSurface(drawingSurface);

            if (startPoint3D == null || endPoint3D == null) throw new Exception("Points must be on specified surface");

            var start = Camera.ProjectPoint(startPoint3D.Value);
            var end = Camera.ProjectPoint(endPoint3D.Value);

            if (start == null || end == null) return null;

            var xMultiplier = Width/Camera.ViewingPlane.VectorX.Length;
            var yMultiplier = Height/Camera.ViewingPlane.VectorY.Length;

            start = new Point {X = start.Value.X*xMultiplier, Y = start.Value.Y*yMultiplier};
            end = new Point {X = end.Value.X*xMultiplier, Y = end.Value.Y*yMultiplier};

            return drawing => drawing.DrawLine(start.Value, end.Value);
        }
    }
}